﻿namespace Cellfish.Toolbox.Facts.Slow.Streams
{
    using System;
    using System.Text;

    using Cellfish.Toolbox.Streams;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class ProducerConsumerStreamFacts
    {
        private ProducerConsumerStream stream = new ProducerConsumerStream();

        private byte[] someData = UTF8Encoding.UTF8.GetBytes("SomeData");

        private byte[] buffer = new byte[128];

        [TestMethod]
        public void Given_ProducerConsumerStream_When_reading_without_writer_Then_read_is_blocked()
        {
            Assert.IsFalse(this.stream.ReadAsync(this.buffer, 0, this.buffer.Length).Wait(TimeSpan.FromSeconds(1)));
            Assert.IsTrue(this.stream.Pending.WaitOne(TimeSpan.Zero), "There is no pending operation.");
        }

        [TestMethod]
        public void Given_ProducerConsumerStream_When_writing_without_reader_Then_write_is_blocked()
        {
            Assert.IsFalse(this.stream.WriteAsync(this.someData, 0, this.someData.Length).Wait(TimeSpan.FromSeconds(1)));
            Assert.IsTrue(this.stream.Pending.WaitOne(TimeSpan.Zero), "There is no pending operation.");
        }

        [TestMethod]
        public void Given_ProducerConsumerStream_When_writing_and_reader_does_not_read_all_data_Then_write_is_blocked()
        {
            var readTask = this.stream.ReadAsync(this.buffer, 0, this.someData.Length - 1);
            Assert.IsTrue(this.stream.Pending.WaitOne(TimeSpan.Zero), "There is no pending read.");
            var writeTask = this.stream.WriteAsync(this.someData, 0, this.someData.Length);
            Assert.AreEqual(this.someData.Length - 1, readTask.Result, "Read did not complete");
            Assert.IsFalse(writeTask.Wait(TimeSpan.FromSeconds(1)));
        }

        [TestMethod]
        public void Given_ProducerConsumerStream_with_pending_write_When_flushing_Then_flush_completes_and_write_is_still_blocked()
        {
            var writeTask = this.stream.WriteAsync(this.someData, 0, this.someData.Length);
            Assert.IsTrue(this.stream.Pending.WaitOne(TimeSpan.Zero), "There is no pending write.");
            this.stream.Flush();
            Assert.IsFalse(writeTask.Wait(TimeSpan.FromSeconds(1)));
        }
    }
}